package com.example.es02.util;

import cn.hutool.core.bean.BeanUtil;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/*
 * @Author lx
 * @Description //TODO
 * @Date 15:01 2021/7/21
 * 需要先创建被@Document注解修饰的索引类
 **/
@Component
public  class ESUtils<T> {
    @Autowired
    @Qualifier("Client")
    private RestHighLevelClient client;
    @Autowired
    private ElasticsearchRestTemplate template;
    //初始化索引并批量导入
    /*
     * @Author lx
     * @Description //TODO
     * @Date 14:57 2021/7/21
     * @Param [java.lang.Object, java.util.List<java.lang.Object>]
     * 创建索引需要先创建带有@Document注解的索引类
     * mapList为数据库查询到的list集合需与索引类对应
     * @return org.elasticsearch.action.bulk.BulkResponse
     **/
    public BulkResponse initIndexAndPushDate(Object o, List<Object> mapList){
        //根据被@Document注解创建索引
        if(!template.indexExists(o.getClass())){
            template.createIndex(o.getClass());
            template.putMapping(o.getClass());
        }
        //批量插入
        BulkRequest request= new BulkRequest();
        //索引名获取
        String indexName = o.getClass().getAnnotation(Document.class).indexName();
        String ids=getClassAnnotaionId(o);
        mapList.forEach(e->{
            request.add(new UpdateRequest(indexName, BeanUtil.beanToMap(e).get(ids).toString())
                    .doc(BeanUtil.beanToMap(e))
                    .docAsUpsert(true));
        });
        BulkResponse bulk=null;
        try {
            bulk = client.bulk(request, RequestOptions.DEFAULT);
            return bulk;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bulk;
    }
    //更新或插入
    /*
     * @Author lx
     * @Description //TODO
     * @Date 14:59 2021/7/21
     * @Param [java.lang.String, java.lang.Object]
     * id为索引id，o为更新的索引类
     * @return java.lang.Boolean
     **/
    public Boolean updateORinsert(String id,Object o){
        //索引名获取
        String indexName = o.getClass().getAnnotation(Document.class).indexName();
        UpdateQuery updateQuery = new UpdateQuery();
        updateQuery.setClazz(o.getClass());
        updateQuery.setId(id);
        updateQuery.setIndexName(indexName);
        //开启不存在插入
        updateQuery.setDoUpsert(true);
        updateQuery.setUpdateRequest(new UpdateRequest(indexName, id).doc(BeanUtil.beanToMap(o)));
        UpdateResponse update = template.update(updateQuery);
        int successful = update.getShardInfo().getSuccessful();
        if(successful>=0){
            return true;
        }else
            return false;
    }
    //分页查询
    /*
     * @Author lx
     * @Description //TODO
     * @Date 14:59 2021/7/21
     * @Param [java.lang.String, java.lang.String, java.lang.Integer, java.lang.Integer]
     * key为查询参数，selectName为查询字段，size查询个数，page查询页数
     * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
     **/
    public List<Map<String,Object>> selectEs(String key,String selectName,Integer size,Integer page){
        SearchRequest request = new SearchRequest();
        // 构建查询的请求体
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //构建模糊和分词查询
        sourceBuilder.query(QueryBuilders.fuzzyQuery(selectName,key).fuzziness(Fuzziness.ONE));
        sourceBuilder.query(QueryBuilders.matchQuery(selectName,key));
        sourceBuilder.size(size);
        sourceBuilder.from((page-1)*size);
        //查询词高亮
        HighlightBuilder highlightBuilder=new HighlightBuilder();
        highlightBuilder.field(selectName);
        highlightBuilder.requireFieldMatch(false);
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        sourceBuilder.highlighter(highlightBuilder);
        request.source(sourceBuilder);
        List<Map<String,Object>> list=new ArrayList<>();
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            SearchHits hits = response.getHits();
            for (SearchHit hit : hits) {
                //取出原查询词，并替换高亮
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField content = highlightFields.get(selectName);
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                if (content!=null){
                    Text[] fragments = content.fragments();
                    String newTitle = "";
                    for (Text text : fragments) {
                        newTitle +=text;
                    }
                    sourceAsMap.put("content",newTitle);//替换掉原来的内容
                }
                list.add(sourceAsMap);
            }
            return list;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ArrayList();
    }
    //批量删除
    /*
     * @Author lx
     * @Description //TODO
     * @Date 15:00 2021/7/21
     * @Param [java.lang.String, java.lang.String...]
     * index为删除的索引，key为删除id
     * @return org.elasticsearch.action.bulk.BulkResponse
     **/
    public BulkResponse deleteES(String index, String[] key){
        //构建批量操作
        BulkRequest request= new BulkRequest();
        for (String e:key){
            request.add(new DeleteRequest(index,e));
        }
        BulkResponse bulk = null;
        try {
            bulk = client.bulk(request, RequestOptions.DEFAULT);
            return bulk;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bulk;
    }

    //获取注解中被@Id的字段名
    private String getClassAnnotaionId(Object o){
        String id="id";
        Field[] declaredFields = o.getClass().getDeclaredFields();
        for (Field e:declaredFields){
            boolean annotation = e.getAnnotation(Id.class) instanceof Id;
            if(annotation) {
                id=e.getName();
                break;
            }
        }
        return id;
    }
}
